/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#ifndef __M2_MODEL_H__
#define __M2_MODEL_H__

#include "GPrerequisites.h"
#include "M2File.h"
#include "M2SkinFile.h"
#include "M2AnimFile.h"
#include "M2ModelInfo.h"
#include "M2MaterialLoader.h"

namespace GNgine
{
	enum M2SubMeshType
	{
		M2_SUB_TYPE_BODY,
		M2_SUB_TYPE_HAIR,

	};

	struct M2ModelSubMesh
	{
		Ogre::String materialName;
		M2SubMeshType type;
		bool show;

		M2ModelSubMesh()
		{
			type=M2_SUB_TYPE_BODY;
			show=false;
		}

	};

	class M2AnimData;

	/*Store intermediate m2 model data
	*/
	class M2Model
	{
	public:
		typedef std::map<std::string,M2Model*> ModelMap;
		typedef std::vector<M2ModelSubMesh> SubMeshList;
		typedef std::vector<unsigned short> BoneHandleList;
		typedef std::vector<M2AnimData*> M2AnimDataList;
	protected:
		M2Model(std::string path);
	public:
		~M2Model();
		
		static M2Model* get(std::string path)
		{
			ModelMap::iterator it=_list.find(path);
			if(it!=_list.end()){
				return it->second;
			}
			M2Model* model=new M2Model(path);
			_list.insert(ModelMap::value_type(path,model));
			return model;
		}

		inline int getBoneCount()
		{
			assert(_m2File);
			return _m2File->getBoneCount();
		}

		inline M2Bone* getM2Bone(int index)
		{
			return _m2File->getBonePtr()+index;
		}

		inline unsigned short getBoneHandle(int index)
		{
			assert(index<_boneHandleList.size()&&index>=0);
			return _boneHandleList[index];
		}
		
		//must add by sequence
		inline void addBoneHandle(unsigned short handle)
		{
			_boneHandleList.push_back(handle);
		}
		

		inline M2AnimFileList getAnimFiles()
		{
			return _animFileList;
		}

		inline std::string getSkeletonName()
		{
			return _path+"/Skeleton";
		}

		inline std::string getMeshName()
		{
			return _path+"/Mesh";
		}
		
		inline M2File* getM2File()
		{
			assert(_m2File);
			return _m2File;
		}

		inline M2SkinFile* getSkinFile()
		{
			assert(_skinFile);
			return _skinFile;
		}

		inline bool isSubMeshShow(int index)
		{
			if(index>=_subMeshes.size()||index<0){
				return false;
			}
			return _subMeshes[index].show;
		}

		inline M2ModelSubMesh& getSubMesh(int index)
		{
			assert(index<_subMeshes.size()&&index>=0);
			return _subMeshes[index];
		}
		inline bool ok()
		{
			return _ok;
		}

		inline int getAnimCount()
		{
			return _animFileList.size();
		}

		inline std::string getAnimName(int index)
		{
			assert(index<_animNameList.size()&&index>=0);
			return _animNameList[index];
		}

		inline M2Animation* getM2Anim(int index)
		{
			assert(index<_animCount&&index>=0);
			return _m2Anims+index;
		}

		inline int getAnimLength(int index)
		{
			assert(index<_animCount&&index>=0);
			return _m2Anims[index].length;
		}

		inline M2AnimData* getBoneTranslateData(int index)
		{
			assert(index<_boneTranslateAnims.size()&&index>=0);
			return _boneTranslateAnims[index];
		}

		inline M2AnimData* getBoneRotateData(int index)
		{
			assert(index<_boneRotateAnims.size()&&index>=0);
			return _boneRotateAnims[index];
		}

		inline M2AnimData* getBoneScaleData(int index)
		{
			assert(index<_boneScaleAnims.size()&&index>=0);
			return _boneScaleAnims[index];
		}

		void chooseHair(int hairStyle);
	private:
		void initModelFiles();
		void initSubMeshes();
		void initCharSubMeshes();
		void initAnims();
		void initBoneAnimData();
	private:
		static ModelMap _list;//
		std::string _path;//m2 file path
		bool _ok;

		int _boneCount;

		M2AnimDataList _boneTranslateAnims;
		M2AnimDataList _boneRotateAnims;
		M2AnimDataList _boneScaleAnims;

		BoneHandleList _boneHandleList;
		
		int _animCount;//model skeleton animation count
		M2Animation* _m2Anims;
		StringVector _animNameList;

		M2File* _m2File;
		M2SkinFile* _skinFile;
		M2AnimFileList _animFileList;

		M2TexUnit* _m2TexUnits;
		M2SubMesh* _m2SubMeshes;
		M2Vertex* _m2Vertices;
		unsigned short *_m2Indices;
		int _m2SubMeshCount;
		int _hairIndex;//current selected hair submesh index

		M2MaterialLoader* _materialLoader;
		Ogre::String _bodyMatName;
		Ogre::String _hairMatName;

		M2ModelInfo* _modelInfo;
		SubMeshList _subMeshes;

	};

}

#endif
